-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Delegation: partial generics support #123958
Conversation
HIR ty lowering was modified cc @fmease |
@rustbot author |
71aa0a9
to
142272a
Compare
@rustbot ready |
I think it would be better to split this work into parts.
|
let generics = delegation_resolver.generics_of(); | ||
let predicates = delegation_resolver.predicates_of(); | ||
|
||
ty::DelegationResults { inputs, output, generics, predicates } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are all components here interdependent and need to be collected together?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They all depend on the pass, which collects parameter definitions and generic arguments. (GenericDefsCollector
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe that pass should be a query instead of lower_delegation_ty
, and the later parts can be just called from predicates_of
/etc because they don't need memoization.
I also suspect that the data from GenericDefsMap
is already collected and available from elsewhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also suspect that the data from GenericDefsMap is already collected and available from elsewhere.
Inference variables are visible only in type check. Entire body type checking is not possible due to a query cycle on the variance pass. Therefore, if there are any data structures, they are not available to us.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe that pass should be a query instead of lower_delegation_ty, and the later parts can be just called from predicates_of/etc because they don't need memoization.
Maybe. I chose this option because all the logic is localized in one module and it is easy to maintain.
I did a high level review, but didn't yet read |
The explainer should say something about what happens with defaults for generic parameters. |
142272a
to
dab1670
Compare
From an implementation perspective, there is no difference between free fn - free fn and free fn - trait method delegation. Technically, I can split this PR into 2 parts:
But I don't understand why the support for generic arguments(explicit inference variables) should be postponed. |
dab1670
to
ba05af3
Compare
This is a separate feature with separate logic (type walking) that has lower priority because it's less likely to be used with glob delegation. Without it generic arguments (including qpaths) could even be removed from the delegation syntax. |
TypeckRootCtxt::new_with_infcx(self.tcx, self.def_id, infcx); | ||
let param_env = ty::ParamEnv::empty(); | ||
let fcx = FnCtxt::new(&root_ctxt, param_env, self.def_id); | ||
fcx.check_expr_with_expectation_and_args( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should ever type check bodies to produce signatures, only signatures should be analyzed to produce signatures.
(Well, until we start implementing step 3, but I'm not sure we even need that part anymore.)
ba05af3
to
602cde9
Compare
This comment has been minimized.
This comment has been minimized.
602cde9
to
f02af50
Compare
This comment has been minimized.
This comment has been minimized.
f02af50
to
a4f8ae0
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some suggestions, comments and questions. I haven't looked at all the changes yet. Nor have I evaluated the overall design/architecture/approach you've taken.
I'm surprised that we need to typeck the body as petrochenkov has mentioned. I wouldn't've expected it to be necessary. However, I can't elaborate on that just yet as I need to go over all the design constraints and the implementation again and have a think. This is just a drive-by review.
fn collect(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> GenericDefsMap<'tcx> { | ||
let mut collector = GenericDefsCollector::new(tcx, def_id); | ||
let caller_kind = fn_kind(tcx, def_id.into()); | ||
// generics are not yet supported for associated delegation items |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// generics are not yet supported for associated delegation items | |
// FIXME(fn_delegation): Support generics on associated delegation items. |
} | ||
|
||
fn fn_kind<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> FnKind { | ||
assert!(matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
assert!(matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn)); | |
debug_assert!(matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn)); |
maybe
if caller_kind == FnKind::Free { | ||
let path = collector.check_call(); | ||
path.visit_with(&mut collector); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe panic or error if caller_kind != FnKind::Free
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Error was reported earlier in check_constraints
. I added a comment.
|
||
// Collect generic parameters definitions during callee type traversal according to | ||
// encountered inference variables. | ||
fn extract_info_from_def(&mut self, def_id: DefId, args: GenericArgsRef<'tcx>) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This currently doesn't support const params (const generics), is that intentional? If so, it should at least panic or error when encountering them. Keep other rustc devs in mind that won't be super familiar with fn_delegation
but might still stumble upon this code for various reasons. It should be self-documenting (via code and comments).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Const parameters are not inherited, but they are allowed in callee path. I added a comment and updated the explainer.
match ty.kind() { | ||
ty::Adt(adt, args) => self.extract_info_from_def(adt.did(), args), | ||
ty::FnDef(did, args) => self.extract_info_from_def(*did, args), | ||
_ => {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've only read so far. What happens if the delegation path contains _
in "unexpected" positions? Imo, we should emit an error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I've read the RFC a while ago and skimmed your explainer, I don't remember the expected behavior of _
inside delegation paths off the top of my head. I'm surprised that we need to match on the ty.kind()
here at all, this doesn't seem very general.
Having briefly read some of the comments inside this file, it seems like _
s are expected to map to fresh type/const parameters? If so, I don't think your current setup supports reuse func::<(_, _)>;
, reuse func::<&_>;
or reuse func::<dyn Trait<_>>;
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be nice to document any such constraints inside this file. Apologies if my observations are a short-sighted or outright unnecessary, I didn't have the time to familiarize myself with all the details of fn delegation yet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, all the above cases should be supported, I overlooked this. We decided to postpone _
syntax until generics are supported in all contexts. I'm going to return to this issue later.
self.tcx, | ||
ty::EarlyParamRegion { index: rid.as_u32(), name: param.name }, | ||
) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(as noted above, lacks support for const generics)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a comment and updated the explainer.
let args = self.tcx.mk_args_from_iter(std::iter::once(generic_self_ty)); | ||
caller_sig.instantiate(self.tcx, args) | ||
} | ||
// here generics are not yet supported |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
panic or error instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Error was reported earlier in check_constraints
. I added a comment.
) -> ty::LoweredDelegation<'tcx> { | ||
let info = GenericDefsCollector::collect(tcx, def_id); | ||
let delegation_resolver = DelegationLowerer::new(tcx, def_id, &info); | ||
if let Err(err) = check_constraints(tcx, def_id) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: this call can be moved to the top now.
a4f8ae0
to
7634ac5
Compare
This comment has been minimized.
This comment has been minimized.
7634ac5
to
f2b1278
Compare
…etrochenkov Delegation: support generics for delegation from free functions (The PR was split from rust-lang#123958, explainer - https://github.com/Bryanskiy/posts/blob/master/delegation%20in%20generic%20contexts.md) This PR implements generics inheritance from free functions to free functions and trait methods. #### free functions to free functions: ```rust fn to_reuse<T: Clone>(_: T) {} reuse to_reuse as bar; // desugaring: fn bar<T: Clone>(x: T) { to_reuse(x) } ``` Generics, predicates and signature are simply copied. Generic arguments in paths are ignored during generics inheritance: ```rust fn to_reuse<T: Clone>(_: T) {} reuse to_reuse::<u8> as bar; // desugaring: fn bar<T: Clone>(x: T) { to_reuse::<u8>(x) // ERROR: mismatched types } ``` Due to implementation limitations callee path is lowered without modifications. Therefore, it is a compilation error at the moment. #### free functions to trait methods: ```rust trait Trait<'a, A> { fn foo<'b, B>(&self, x: A, y: B) {...} } reuse Trait::foo; // desugaring: fn foo<'a, 'b, This: Trait<'a, A>, A, B>(this: &This, x: A, y: B) { Trait::foo(this, x, y) } ``` The inheritance is similar to the previous case but with some corrections: - `Self` parameter converted into `T: Trait` - generic parameters need to be reordered so that lifetimes go first Arguments are similarly ignored. --- In the future, we plan to support generic inheritance for delegating from all contexts to all contexts (from free/trait/impl to free/trait /impl). These cases were considered first as the simplest from the implementation perspective.
Delegation: support generics for delegation from free functions (The PR was split from rust-lang/rust#123958, explainer - https://github.com/Bryanskiy/posts/blob/master/delegation%20in%20generic%20contexts.md) This PR implements generics inheritance from free functions to free functions and trait methods. #### free functions to free functions: ```rust fn to_reuse<T: Clone>(_: T) {} reuse to_reuse as bar; // desugaring: fn bar<T: Clone>(x: T) { to_reuse(x) } ``` Generics, predicates and signature are simply copied. Generic arguments in paths are ignored during generics inheritance: ```rust fn to_reuse<T: Clone>(_: T) {} reuse to_reuse::<u8> as bar; // desugaring: fn bar<T: Clone>(x: T) { to_reuse::<u8>(x) // ERROR: mismatched types } ``` Due to implementation limitations callee path is lowered without modifications. Therefore, it is a compilation error at the moment. #### free functions to trait methods: ```rust trait Trait<'a, A> { fn foo<'b, B>(&self, x: A, y: B) {...} } reuse Trait::foo; // desugaring: fn foo<'a, 'b, This: Trait<'a, A>, A, B>(this: &This, x: A, y: B) { Trait::foo(this, x, y) } ``` The inheritance is similar to the previous case but with some corrections: - `Self` parameter converted into `T: Trait` - generic parameters need to be reordered so that lifetimes go first Arguments are similarly ignored. --- In the future, we plan to support generic inheritance for delegating from all contexts to all contexts (from free/trait/impl to free/trait /impl). These cases were considered first as the simplest from the implementation perspective.
@Bryanskiy any updates on this? thanks |
This needs to be significantly reworked, but I don't have time for it right now. I'll come back to it later. |
PR explainer: https://github.com/Bryanskiy/posts/blob/master/delegation%20in%20generic%20contexts.md
Part of #118212